package edu.ntu.cloudroid.model.data;
import java.io.PrintWriter;
import java.io.Serializable;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Random;

import javax.jdo.annotations.FetchGroup;
import javax.jdo.annotations.Inheritance;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;


import com.sun.xacml.attr.DoubleAttribute;
import com.sun.xacml.ctx.Attribute;
import com.sun.xacml.ctx.RequestCtx;
import com.sun.xacml.ctx.Subject;

import edu.ntu.cloudroid.model.IDs;
import edu.ntu.cloudroid.model.profile.ProfileData;
import edu.ntu.cloudroid.nongui.com.Communicator;
import edu.ntu.cloudroid.server.backendservice.ProfileService;
import edu.ntu.cloudroid.xacml.XACMLStrings;

@PersistenceCapable(detachable="true")
@Inheritance
@FetchGroup(name = AbstractData.FETCH_GROUP_NAME, members = { @Persistent(name = "person") })
public class VitalSignData extends AbstractData{

	private static final long serialVersionUID = 1L;
	
	public static final int HEART_RATE = 0;
	public static final int BLOOD_PRESSURE = 1;
	public static final int SUMMARY = 2;
	
	private static final int MAX_HR = 100;
	private static final int MAX_BP = 100;
	private static int[] RANGES = {0,20,30};
	
	@Persistent
	private double heartRate, oxygenVolume;	
	
	public VitalSignData(){}
		
	public VitalSignData(String name, double hr, double bp, Date ts) throws Exception{
		//this.setProfile(ProfileService.getProfile(name));
		this.setName(name);
		ProfileData p = null;
		if (!name.equals("") && !name.startsWith("null")) {
			this.setName(name);
		}
		this.setTimestamp(ts);
		this.heartRate = hr;
		this.oxygenVolume = bp;
	}	
	
	@Override
	public int getDataId(){
		return IDs.DataID.VitalSignData.ordinal();
	}
	public double getHeartRate(){
		return this.heartRate;
	}

	public double getOxygenVolume(){
		return this.oxygenVolume;
	}
		
	

	public void setHeartRate(double heartRate) {
		this.heartRate = heartRate;
	}

	public void setOxygenVolume(double ov) {
		this.oxygenVolume = ov;
	}

	@Override
	public DetachedData detach(Object params) {
		int colIdx = ((Integer)params).intValue();
		VitalSignInfo vsi = null;
		if (colIdx==0)
			vsi = new VitalSignInfo(this.getName(), this.getTimestamp(),
					this.getHeartRate(), -1);
		else
			vsi = new VitalSignInfo(this.getName(), this.getTimestamp(),
					-1, this.getOxygenVolume());
		return vsi;
	}
	
	@Override
	public int compareTo(AbstractData v, int idx) {
		double value,val;
		if (idx==VitalSignData.HEART_RATE){
			value = this.getHeartRate();
			val = ((VitalSignData)v).getHeartRate();
		}
		else{
			value = this.getOxygenVolume();
			val = ((VitalSignData)v).getOxygenVolume();
		}
		return compare(value,val);
	}

	@Override
	public int compareTo(String v, int idx){
		double val = new Double(v).doubleValue();
		double value;
		if (idx==VitalSignData.HEART_RATE)
			value = this.getHeartRate();
		else
			value = this.getOxygenVolume();
		return compare(value,val);
	}
	
	private int compare(double value, double val){
		if (value<=val)
			return -1;
		else if (value == val)
			return 0;
		else 
			return 1;
	}
	
	
	@Override
	public AbstractData initialize(String[] entries) throws Exception {
		//the format will be <data type> <name> <type> <value> <time>		
		VitalSignData vsd = new VitalSignData();
		//vsd.setProfile(p);
		vsd.setName(entries[1]);
		vsd.setHeartRate(new Double(entries[2]).doubleValue());
		vsd.setOxygenVolume(new Double(entries[3]).doubleValue());
		vsd.setTimestamp(new SimpleDateFormat("yyyy-MM-dd HH:mm")
						.parse(entries[4]));
		return vsd;
	}

	@Override
	public Object getWindowAverage(List<AbstractData> list, int colIndex) throws Exception{
		//sliding window summary
		double sum = 0;
		double val=0;
		for (AbstractData ad : list){
			val = colIndex == VitalSignData.HEART_RATE ? ((VitalSignData) ad)
					.getHeartRate() : ((VitalSignData) ad).getOxygenVolume();
			sum = sum + val;
		}
		if (colIndex==VitalSignData.HEART_RATE)
			return new VitalSignData(this.getName(),sum/list.size(), -1, new Date()); 
		else
			return new VitalSignData(this.getName(),-1,sum / list.size(), new Date());
	}

	@Override
	public AbstractData getInstance() {
		return new VitalSignData();
	}

	
	@Override
	public Object changeGranuality(int granuality, int colIdx) throws Exception {
		if (granuality==0)
			return this.detach(colIdx);		
		VitalSignInfo vsi;
		int err = RANGES[granuality];
		if (colIdx == 0)
			vsi = new VitalSignInfo(this.getName(), this.getTimestamp(),
					Math.max(0, this.heartRate - err), Math.min(100,
							this.heartRate + err), -1, -1);
		else if (colIdx==1)
			vsi = new VitalSignInfo(this.getName(), this.getTimestamp(), -1,
					-1, Math.max(0, this.oxygenVolume - err), Math.min(100,
							this.oxygenVolume + err));
		else
			vsi = vsi = new VitalSignInfo(this.getName(), this.getTimestamp(),
					this.getHeartRate(), this.getOxygenVolume());
		return vsi;		
	}

	@Override
	public AbstractData getDataFromRequest(RequestCtx requestCtx)
			throws Exception {
		Subject subject = (Subject)requestCtx.getSubjects().iterator().next();
		Attribute att;
		double hr=0, bp=0;
		for (Object obj : subject.getAttributes()){
			att = (Attribute) obj;
			if (att.getId().equals(new URI(XACMLStrings.ATTR_HeartRate)))
				hr = ((DoubleAttribute)att.getValue()).getValue();
			else if (att.getId().equals(new URI(XACMLStrings.ATTR_BloodPressure))){
				bp = ((DoubleAttribute)att.getValue()).getValue();
			}
		}
		return new VitalSignData("", hr, bp, new Date());
	}
		
}
